package com.study.chunguard.common.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;

/**
 * 时间范围枚举
 *
 * @author zhaowenhao
 * @since 2023-11-02
 */
@Getter
@AllArgsConstructor
public enum TimeRangeEnum {

    /**
     * 近30天，但不包括当日
     */
    LAST_30_DAYS_EXCLUDE_TODAY("LAST_30_DAYS_EXCLUDE_TODAY"),

    /**
     * 近6个月，包括本月，但不包括当日
     */
    LAST_6_MONTHS_INCLUDE_CURRENT_MONTH_EXCLUDE_TODAY("LAST_6_MONTHS_INCLUDE_CURRENT_MONTH_EXCLUDE_TODAY"),

    /**
     * 近7天，不包括当日
     */
    LAST_7_DAYS_EXCLUDE_TODAY("LAST_7_DAYS_EXCLUDE_TODAY"),

    /**
     * 近180天，不包括当日
     */
    LAST_180_DAYS_EXCLUDE_TODAY("LAST_180_DAYS_EXCLUDE_TODAY"),

    /**
     * 本周，不包括当日
     */
    CURRENT_WEEK_EXCLUDE_TODAY("CURRENT_WEEK_EXCLUDE_TODAY"),

    /**
     * 本月，不包括当日
     */
    CURRENT_MONTH_EXCLUDE_TODAY("CURRENT_MONTH_EXCLUDE_TODAY"),

    /**
     * 本年，不包括当日
     */
    CURRENT_YEAR_EXCLUDE_TODAY("CURRENT_YEAR_EXCLUDE_TODAY"),

    /**
     * 去年本周，不包括当日
     */
    LAST_YEAR_CURRENT_WEEK_EXCLUDE_TODAY("LAST_YEAR_CURRENT_WEEK_EXCLUDE_TODAY"),

    /**
     * 去年本月，不包括当日
     */
    LAST_YEAR_CURRENT_MONTH_EXCLUDE_TODAY("LAST_YEAR_CURRENT_MONTH_EXCLUDE_TODAY"),

    /**
     * 去年本年，不包括当日
     */
    LAST_YEAR_CURRENT_YEAR_EXCLUDE_TODAY("LAST_YEAR_CURRENT_YEAR_EXCLUDE_TODAY"),

    /**
     * 上周，不包括上周当日
     */
    LAST_WEEK_EXCLUDE_TODAY("LAST_WEEK_EXCLUDE_TODAY"),

    /**
     * 上月，不包括上月当日
     */
    LAST_MONTH_EXCLUDE_TODAY("LAST_MONTH_EXCLUDE_TODAY"),

    /**
     * 上年，不包括上年当日
     */
    LAST_YEAR_EXCLUDE_TODAY("LAST_YEAR_EXCLUDE_TODAY"),

    /**
     * 近7天，不包括当日
     */
    TODAY("TODAY");


    /**
     * 名称
     */
    private final String name;

    @Override
    public String toString() {
        return name;
    }

    public static TimeRangeEnum convertFromName(String name) {
        Optional<TimeRangeEnum> first = Arrays.stream(TimeRangeEnum.values()).filter(anEnum -> anEnum.getName().equals(name)).findFirst();
        if (first.isPresent()) {
            return first.get();
        }
        throw new IllegalArgumentException("时间范围错误");
    }

    /**
     * yyyy格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_YEAR = DateTimeFormatter.ofPattern("yyyy");

    /**
     * yyyy-MM格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_MONTH = DateTimeFormatter.ofPattern("yyyy-MM");

    /**
     * yyyy-MM-dd格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * yyyy-MM-dd HH格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_HOUR = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");

    /**
     * yyyy-MM-dd HH:mm格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_MINUTE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    /**
     * yyyy-MM-dd HH:mm:ss格式化，DateTimeFormatter线程安全
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER_LEVEL_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 保留到年的字符串时间范围
     */
    public StringTimeRange getYearStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_YEAR);
    }

    /**
     * 保留到月的字符串时间范围
     */
    public StringTimeRange getMonthStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_MONTH);
    }

    /**
     * 保留到日的字符串时间范围
     */
    public StringTimeRange getDayStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_DAY);
    }

    /**
     * 保留到小时的字符串时间范围
     */
    public StringTimeRange getHourStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_HOUR);
    }

    /**
     * 保留到分钟的字符串时间范围
     */
    public StringTimeRange getMinuteStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_MINUTE);
    }

    /**
     * 保留到秒的字符串时间范围
     */
    public StringTimeRange getSecondStringTimeRange() {
        return getStringTimeRange(DATE_TIME_FORMATTER_LEVEL_SECOND);
    }

    /**
     * 字符串日期范围, 格式为yyyy-MM-dd HH:mm:ss 中的若干部分，依据 formatter 决定
     *
     * @param formatter 格式化器
     * @return 字符串日期范围
     */
    private StringTimeRange getStringTimeRange(DateTimeFormatter formatter) {
        LocalDateTime[] dateTimeRange = calculateDateTimeRange();
        return new StringTimeRange(
                formatter.format(dateTimeRange[0]),
                formatter.format(dateTimeRange[1])
        );
    }

    /**
     * Java日期范围
     *
     * @return Java日期范围
     */
    public JdkDateRange getJdkDateRange() {
        LocalDateTime[] dateTimeRange = calculateDateTimeRange();
        return new JdkDateRange(
                Date.from(dateTimeRange[0].atZone(ZoneId.systemDefault()).toInstant()),
                Date.from(dateTimeRange[1].atZone(ZoneId.systemDefault()).toInstant())
        );
    }

    private LocalDateTime[] calculateDateTimeRange() {
        // 当前的准确时间，备用
        LocalDateTime nowTime = LocalDateTime.now();

        // 今天
        LocalDate nowDay = LocalDate.now();
        // 昨天
        LocalDate lastDay = nowDay.minusDays(1);
        // 今天往前推7天
        LocalDate last7Day = nowDay.minusDays(7);
        // 今天往前推30天
        LocalDate last30Day = nowDay.minusDays(30);
        // 今天往前推180天
        LocalDate last180Day = nowDay.minusDays(180);
        // 往前推5个月的第一天
        LocalDate last5MonthFirstDay = nowDay.minusMonths(6).withDayOfMonth(1);
        // 本周第一天
        LocalDate currentWeekFirstDay = nowDay.with(DayOfWeek.MONDAY);
        // 本月第一天
        LocalDate currentMonthFirstDay = nowDay.with(TemporalAdjusters.firstDayOfMonth());
        // 本年第一天
        LocalDate currentYearFirstDay = nowDay.with(TemporalAdjusters.firstDayOfYear());

        // 去年的今天
        LocalDate lastYearNowDay = LocalDate.now().minusYears(1);
        // 去年的昨天
        LocalDate lastYearLastDay = lastYearNowDay.minusDays(1);

        // 去年本周第一天
        LocalDate lastYearCurrentWeekFirstDay = lastYearNowDay.with(DayOfWeek.MONDAY);
        // 去年本周的昨天
        LocalDate lastYearCurrentWeekYesterday = lastYearLastDay.with(lastDay.getDayOfWeek());
        // 去年本月第一天
        LocalDate lastYearCurrentMonthFirstDay = lastYearNowDay.with(TemporalAdjusters.firstDayOfMonth());
        // 去年本年第一天
        LocalDate lastYearCurrentYearFirstDay = lastYearNowDay.with(TemporalAdjusters.firstDayOfYear());

        // 上周的第一天
        LocalDate lastWeekFirstDay = nowDay.minusWeeks(1).with(DayOfWeek.MONDAY);
        // 上周的昨天
        LocalDate lastWeekYesterday = lastDay.minusWeeks(1);

        // 上月第一天
        LocalDate lastMonthFirstDay = nowDay.minusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
        // 上月的昨天
        LocalDate lastMonthYesterday = lastDay.minusMonths(1);

        // 上年的第一天
        LocalDate lastYearFirstDay = nowDay.minusYears(1).with(TemporalAdjusters.firstDayOfYear());
        // 上年的昨天
        LocalDate lastYearYesterday = lastDay.minusYears(1);

        LocalDateTime timeStart;
        LocalDateTime timeEnd;

        switch (this) {
            case TODAY:
                timeStart = nowDay.atStartOfDay();
                timeEnd = nowDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_7_DAYS_EXCLUDE_TODAY:
                timeStart = last7Day.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_30_DAYS_EXCLUDE_TODAY:
                timeStart = last30Day.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_180_DAYS_EXCLUDE_TODAY:
                timeStart = last180Day.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_6_MONTHS_INCLUDE_CURRENT_MONTH_EXCLUDE_TODAY:
                timeStart = last5MonthFirstDay.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case CURRENT_WEEK_EXCLUDE_TODAY:
                timeStart = currentWeekFirstDay.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case CURRENT_MONTH_EXCLUDE_TODAY:
                timeStart = currentMonthFirstDay.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case CURRENT_YEAR_EXCLUDE_TODAY:
                timeStart = currentYearFirstDay.atStartOfDay();
                timeEnd = lastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_YEAR_CURRENT_WEEK_EXCLUDE_TODAY:
                timeStart = lastYearCurrentWeekFirstDay.atStartOfDay();
                timeEnd = lastYearCurrentWeekYesterday.atTime(23, 59, 59, 999999999);
                break;
            case LAST_YEAR_CURRENT_MONTH_EXCLUDE_TODAY:
                timeStart = lastYearCurrentMonthFirstDay.atStartOfDay();
                timeEnd = lastYearLastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_YEAR_CURRENT_YEAR_EXCLUDE_TODAY:
                timeStart = lastYearCurrentYearFirstDay.atStartOfDay();
                timeEnd = lastYearLastDay.atTime(23, 59, 59, 999999999);
                break;
            case LAST_WEEK_EXCLUDE_TODAY:
                timeStart = lastWeekFirstDay.atStartOfDay();
                timeEnd = lastWeekYesterday.atTime(23, 59, 59, 999999999);
                break;
            case LAST_MONTH_EXCLUDE_TODAY:
                timeStart = lastMonthFirstDay.atStartOfDay();
                timeEnd = lastMonthYesterday.atTime(23, 59, 59, 999999999);
                break;
            case LAST_YEAR_EXCLUDE_TODAY:
                timeStart = lastYearFirstDay.atStartOfDay();
                timeEnd = lastYearYesterday.atTime(23, 59, 59, 999999999);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + this);
        }
        return new LocalDateTime[]{timeStart, timeEnd};
    }


    /**
     * Java日期范围
     */
    @Getter
    @AllArgsConstructor
    public static class JdkDateRange {
        private final Date timeStart;
        private final Date timeEnd;
    }

    /**
     * 字符串日期范围, 格式为yyyy-MM-dd HH:mm:ss 中的若干部分，依据 DATE_TIME_FORMATTER_LEVEL_XXX 格式决定
     */
    @Getter
    @AllArgsConstructor
    public static class StringTimeRange {
        private final String timeStart;
        private final String timeEnd;

    }

    public static void main(String[] args) {
        for (TimeRangeEnum timeRange : TimeRangeEnum.values()) {
            System.out.println("枚举值: " + timeRange);

            JdkDateRange jdkDateRange = timeRange.getJdkDateRange();
            Date timeStart = jdkDateRange.getTimeStart();
            Date timeEnd = jdkDateRange.getTimeEnd();
            System.out.println("Java日期范围: 开始时间 = " + timeStart + ", 结束时间 = " + timeEnd);

            StringTimeRange stringTimeRange = timeRange.getSecondStringTimeRange();
            System.out.println("保留到秒的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            stringTimeRange = timeRange.getMinuteStringTimeRange();
            System.out.println("保留到分钟的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            stringTimeRange = timeRange.getHourStringTimeRange();
            System.out.println("保留到小时的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            stringTimeRange = timeRange.getDayStringTimeRange();
            System.out.println("保留到日的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            stringTimeRange = timeRange.getMonthStringTimeRange();
            System.out.println("保留到月的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            stringTimeRange = timeRange.getYearStringTimeRange();
            System.out.println("保留到年的字符串时间范围: 开始时间 = " + stringTimeRange.getTimeStart() + ", 结束时间 = " + stringTimeRange.getTimeEnd());

            System.out.println("=============================================");
        }
    }
}
